package com.nali.dal.statement;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.nali.dal.exception.TooManyResultException;
import com.nali.dal.expression.ExpressionValue;
import com.nali.dal.expression.query.Criteria;
import com.nali.dal.statement.query.SelectStatement;
import com.nali.dal.statement.query.Selecter;
import com.nali.dal.statement.selectupsert.OperateSelectUpserterStatement;
import com.nali.dal.statement.selectupsert.SelectUpserter;
import com.nali.dal.statement.upsert.Inserter;
import com.nali.dal.statement.upsert.InserterStatement;
import com.nali.dal.statement.upsert.OperateUpsertStatement;
import com.nali.dal.statement.upsert.Upserter;

public class DalTemplateImpl implements DalTemplate {
	@Autowired
	private BeanFactory beanFactory;
	private String prefix;
	private ConcurrentHashMap<String, Object> caches = new ConcurrentHashMap<String, Object>();
	
	private <T> T getStatement(String statement, Class<T> clazz) {
		String beanName;
		if(prefix!=null) {
			beanName = prefix+statement;
		} else {
			beanName = statement;
		}
		try {
			return beanFactory.getBean(beanName, clazz);
		} catch (BeansException e) {
			throw new IllegalArgumentException("cannot find statement:" + statement, e);
		}
	}
	
	private Selecter getSelecter(String statement) {
		Object obj = caches.get(statement);
		if(obj==null) {
			obj = getStatement(statement, SelectStatement.class).getSelecter();
			caches.putIfAbsent(statement, obj);
		}
		return (Selecter) obj;
	}
	
	private Upserter getUpserter(String statement) {
		Object obj = caches.get(statement);
		if(obj==null) {
			obj = getStatement(statement, OperateUpsertStatement.class).getUpserter();
			caches.putIfAbsent(statement, obj);
		}
		return (Upserter) obj;
	}
	
	private Inserter getInserter(String statement) {
		Object obj = caches.get(statement);
		if(obj==null) {
			obj = getStatement(statement, InserterStatement.class).getInserter();
			caches.putIfAbsent(statement, obj);
		}
		return (Inserter) obj;
	}
	
	private SelectUpserter getSelectUpserter(String statement) {
		Object obj = caches.get(statement);
		if(obj==null) {
			obj = getStatement(statement, OperateSelectUpserterStatement.class).getSelectUpserter();
			caches.putIfAbsent(statement, obj);
		}
		return (SelectUpserter) obj;
	}

	@Override
	public long count(String statement, ExpressionValue<Criteria>... conds) {
		return getSelecter(statement).count(conds);
	}
	
	@Override
	public List<Map<String, Object>> select(String statement, ExpressionValue<Criteria>... conds) {
		return getSelecter(statement).execute(conds);
	}

	@Override
	public List<Map<String, Object>> select(String statement, ExpressionValue<Criteria>[] conds, int offset, int limit) {
		return getSelecter(statement).execute(conds, offset, limit);
	}

	@Override
	public int selectUpsert(String statement, ExpressionValue<Criteria>... conds) {
		return getSelectUpserter(statement).selectUpsert(conds);
	}

	@Override
	public void insert(String statement, Map<String, Object> lineDatas) {
		getInserter(statement).upsert(lineDatas);
	}

	@Override
	public int upsert(String statement, Map<String, Object> lineDatas) {
		return getUpserter(statement).upsert(lineDatas);
	}

	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}

	@Override
	public Map<String, Object> selectForObject(String statement, ExpressionValue<Criteria>... conds) {
		List<Map<String, Object>> list = select(statement, conds);
		if(list.size()==0) {
			return null;
		} else if(list.size()==1) {
			return list.get(0);
		} else {
			throw new TooManyResultException("rlt size > 1:" + statement);
		}
	}

	@Override
	public SelectBuilder startFor(String statement) {
		return new SelectBuilder(this, statement);
	}

}
